var util = require('util');
var _ = require('./underscore');
var Formatter = require('./formatter');
var MessagePack = null; // lazy load of require('msgpack')

exports['json'] = {
  description: "The default format.  Outputs strictly correct, human-readible JSON w/ smart whitespace. This format has received a lot of love.  Try the '--color' flag.",
  stringify: Formatter.withConfig({
    color: false,
    jsonNullBehavior: true, // add 'null' to array when elements are unset or equal to 'undefined' (distinct cases); skip object properties that map to 'undefined'
    jsonIgnoreArrayProps: true, // In JS, it's possible to set properties on an Array object.  E.g., (v=[1, 2, 3, 4],v.prop1=true,v).  JSON.stringify ignores these.  When this behavior is 'true', so do we
    quoteKeys: true,
    quotes: '"',
    wrapWidth: 100,
    openBraceOnNewline: false
  })
};

exports['dense'] = {
  description: "Output dense JSON using JSON.stringify.  Efficient, but hard to read.",
  stringify: function (data) {
    return JSON.stringify(data);
  }
};

exports['stringify'] = {
  description: "Output formatted JSON using JSON.stringify.  A bit too verbose.",
  stringify: function (data) {
    return JSON.stringify(data, null, '  ');
  }
};

exports['pretty'] = {
  description: "Output a richer 'inspection' syntax.  When printing array-and-object graphs that can be generated by JSON.parse, the output is valid JavaScript syntax (but not strict JSON).  When handling complex objects not expressable in declarative JavaScript (eg arrays that also have object properties), the output is informative, but not parseable as JavaScript.",
  stringify: Formatter.withConfig({
    color: true,
    jsonParity: false,
    quoteKeys: false,
    quotes: '"',
    wrapWidth: 100,
    openBraceOnNewline: false,
    complexObjects: true
  })
};

exports['inspect'] = {
  description: "Uses Node's 'util.inspect' to print the output",
  stringify: function (data) {
    return util.inspect(data, false, 9999);
  }
};

exports['text'] = {
  description: "If data is a string, it is printed directly without quotes.  If data is an array, elements are separated by newlines.  Objects and arrays-within-arrays are JSON formated into a single line.  The stock example does not convey the intent of this format, which is designed to enable traditional text processing via JavaScript and to facilitate flattening of JSON lists into line-delimited lists.",
  stringify: function (data) {
    if (! _.isArray(data)) {
      data = [data];
    }
    return _.map(data, function (entry) {
      if (_.isString(entry)) {
        return entry;
      } else {
        return JSON.stringify(entry);
      }
    }).join('\n');
  }
};

exports['msgpack'] = {
  description: 'MessagePack binary JSON format',
  stringify: function (text) {
    if (! MessagePack) {
      MessagePack = require('msgpack');
    }
    return MessagePack.pack(text);
  }
};


function bold (string) {
  // bold white.  see https://github.com/gf3/coloured/blob/master/lib/coloured.js
  return "\033[1;37m" + string + "\033[0m";
}


exports['msgpack.print'] = {
  description: 'Textual representation of MessagePack',
  stringify: function (text) {
    if (! MessagePack) {
      MessagePack = require('msgpack');
    }
    return MessagePack.pack(text)
      .toString('binary')
      .replace(/./g, function (x) {
        var cc = x.charCodeAt(0);
        if (cc > 31 && cc < 127) {
          return x;
        } else {
          if (cc < 16) {
            return bold("<0" + cc.toString(16) + ">");
          } else {
            return bold("<" + cc.toString(16) + ">");
          }
        }
      });
  }
};
